package com.qyb.base.java8.chap3;



import com.qyb.base.java8.bean.Apple;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

/*
	Java API中已经有了几个函数式接口，比如你在3.2节中见到的Comparable、Runnable和Callable。
	Java 8的库设计师帮你在java.util.function包中引入了几个新的函数式接口。我们接下来会介绍Predicate、Consumer和Function

	Predicate：
		java.util.function.Predicate<T>接口定义了一个名叫test的抽象方法，它接受泛型
		T对象，并返回一个boolean。这恰恰和你先前创建的一样，现在就可以直接使用了。在你需要
		表示一个涉及类型T的布尔表达式时，就可以使用这个接口。比如，你可以定义一个接受String
		对象的Lambda表达式，如下所示。

		@FunctionalInterface
		public interface Predicate<T>{
			boolean test(T t);
		}
		public static <T> List<T> filter(List<T> list, Predicate<T> p) {
			List<T> results = new ArrayList<>();
			for(T s: list){
				if(p.test(s)){
					results.add(s);
				}
			}
			return results;
		}
		Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
		List<String> nonEmpty = filter(listOfStrings,nonEmptyStringPredicate);


	Consumer
		java.util.function.Consumer<T>定义了一个名叫accept的抽象方法，它接受泛型T
		的对象，没有返回（void）。你如果需要访问类型T的对象，并对其执行某些操作，就可以使用
		这个接口。比如，你可以用它来创建一个forEach方法，接受一个Integers的列表，并对其中
		每个元素执行操作。在下面的代码中，你就可以使用这个forEach方法，并配合Lambda来打印
		列表中的所有元素。

		@FunctionalInterface
		public interface Consumer<T>{
			void accept(T t);
		}
		public static <T> void forEach(List<T> list, Consumer<T> c){
			for(T i: list){
				c.accept(i);
			}
		}
		forEach(Arrays.asList(1,2,3,4,5),(Integer i) -> System.out.println(i));//Lambda是Consumer中accept方法的实现

	Function
		java.util.function.Function<T, R>接口定义了一个叫作apply的方法，它接受一个
		泛型T的对象，并返回一个泛型R的对象。如果你需要定义一个Lambda，将输入对象的信息映射
		到输出，就可以使用这个接口（比如提取苹果的重量，或把字符串映射为它的长度）。在下面的
		代码中，我们向你展示如何利用它来创建一个map方法，以将一个String列表映射到包含每个
		String长度的Integer列表。

		@FunctionalInterface
		public interface Function<T, R>{
			R apply(T t);
		}
		public static <T, R> List<R> map(List<T> list, Function<T, R> f) {
			List<R> result = new ArrayList<>();
			for(T s: list){
				result.add(f.apply(s));
			}
			return result;
		}
		// [7, 2, 6]
		List<Integer> l = map( Arrays.asList("lambdas","in","action"), (String s) -> s.length());//Lambda是Function接口的apply方法的实现

		原始类型特化:我们介绍了三个泛型函数式接口：Predicate<T>、Consumer<T>和Function<T,R>。还有些函数式接口专为某些类型而设计
		为了避免装箱操作，对Predicate<T>和Function<T, R>等通用函数式接口的原始类型特化：IntPredicate、IntToLongFunction等。

		public interface IntPredicate{
			boolean test(int t);
		}
		IntPredicate evenNumbers = (int i) -> i % 2 == 0;
		evenNumbers.test(1000);     //true（无装箱）
		Predicate<Integer> oddNumbers = (Integer i) -> i % 2 == 1;
		oddNumbers.test(1000);      //false（装箱）
		针对专门的输入参数类型的函数式接口的名称都要加上对应的原始类型前缀，比如DoublePredicate、IntConsumer、LongBinaryOperator、IntFunction等
		Function接口还有针对输出参数类型的变种：ToIntFunction<T>、IntToDoubleFunction等。


	异常处理：有两种办法：定义一个自己的函数式接口，并声明受检异常，或者把Lambda包在一个try/catch块中。

	方法引用。可以把它们视为某些Lambda的快捷写法。你可以把方法引用看作针对仅仅涉及单一方法的Lambda的语法糖


	有效的lambda表达式举例：
	1.(String s) -> s.length()
		第一个Lambda表达式具有一个String类型的参数并返回一个int。Lambda没有return语句，因为已经隐含了return
	2.(Apple a) -> a.getWeight() > 150
		第二个Lambda表达式有一个Apple类型的参数并返回一个boolean（苹果的重量是否超过150克）
	3.(int x, int y) -> {
		System.out.println("Result:");
		System.out.println(x+y);
	}
		第三个Lambda表达式具有两个int类型的参数而没有返回值（void返回）。注意Lambda表达式可以包含多行语句，这里是两行
	4.() -> 42
		第四个Lambda表达式没有参数，返回一个int
	5.(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())
		第五个Lambda表达式具有两个Apple类型的参数，返回一个int：比较两个Apple的重量


基本语法
	1.(parameters) -> expression
	2.(parameters) -> { statements; }

	使用案例                       Lambda示例
	布尔表达式  			 (List<String> list) -> list.isEmpty()
	创建对象 				 () -> new Apple(10)
	消费一个对象             (Apple a) -> {
				                   System.out.println(a.getWeight());
				              }
	从一个对象中选择/抽取     (String s) -> s.length()
	组合两个值                (int a, int b) -> a * b
	比较两个对象              (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())


使用案例            Lambda的例子                                					对应的函数式接口
布尔表达式  (List<String> list) -> list.isEmpty()               					Predicate<List<String>>
创建对象     () -> new Apple(10)                                					Supplier<Apple>
消费一个对象  (Apple a) -> System.out.println(a.getWeight())    					Consumer<Apple>
从一个对象中选择/提取    String s) -> s.length()                					Function<String, Integer>或ToIntFunction<String>
合并两个值     (int a, int b) -> a * b                                              IntBinaryOperator
比较两个对象  (Apple a1, Apple a2) ->a1.getWeight().compareTo(a2.getWeight())       Comparator<Apple>或BiFunction<Apple, Apple, Integer>或ToIntBiFunction<Apple, Apple>



在哪里以及如何使用Lambda:  你可以在函数式接口上使用Lambda表达式。


 */
public class Lambdas {
	public static void main(String ...args){

		// Simple example
		Runnable r = () -> System.out.println("Hello!");
		r.run();

		// Filtering with lambdas
		List<Apple> inventory = Arrays.asList(new Apple(80,"green"), new Apple(155, "green"), new Apple(120, "red"));

		// [Apple{color='green', weight=80}, Apple{color='green', weight=155}]	
		List<Apple> greenApples = filter(inventory, (Apple a) -> "green".equals(a.getColor()));
		System.out.println(greenApples);


		Comparator<Apple> c = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());

		// [Apple{color='green', weight=80}, Apple{color='red', weight=120}, Apple{color='green', weight=155}]
		inventory.sort(c);
		System.out.println(inventory);
	}

	public static List<Apple> filter(List<Apple> inventory, ApplePredicate p){
		List<Apple> result = new ArrayList<>();
		for(Apple apple : inventory){
			if(p.test(apple)){
				result.add(apple);
			}
		}
		return result;
	}

	interface ApplePredicate{
		public boolean test(Apple a);
	}
}